package com.xpec.c4.service.message;

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jgroups.blocks.RequestOptions;

import com.ms.game.forciblyBulletin.ForciblyBulletinInfo;
import com.ms.player.Player;
import com.ms.service.error.ErrorCode;
import com.ms.service.message.BaseMessage;
import com.ms.service.message.IResultMessageBase;
import com.xpec.c4.codec.CodecUtility;
import com.xpec.c4.game.utility.GUID;
import com.xpec.c4.game.utility.GameUtility;
import com.xpec.c4.h5net.H5XSENetSession;
import com.xpec.c4.message.ConstantCode;
import com.xpec.c4.message.MessageTypes;
import com.xpec.c4.net.XSENetSession;
import com.xpec.c4.service.common.ServerModel;
import com.xpec.c4.service.common.ServiceInfo;
import com.xpec.c4.service.contexts.Contexts;
import com.xpec.c4.service.exceptions.GroupTransportException;
import com.xpec.c4.service.group.ServerGroup;
import com.xpec.c4.service.group.transportlayer.BroadcastTargetTypes;
import com.xpec.c4.service.group.transportlayer.GroupMessage;
import com.xpec.c4.service.h5common.H5ServiceInfo;
import com.xpec.c4.service.h5server.H5ServerOperations;
import com.xpec.c4.service.module.BasicServiceModule;
import com.xpec.c4.service.module.ModuleName;
import com.xpec.c4.service.module.ModuleServer;
import com.xpec.c4.service.module.ServerProperties;
import com.xpec.c4.service.module.common.PlayerCommonInfoManager;
import com.xpec.c4.service.module.gateway.GatewayModule;
import com.xpec.c4.service.module.gateway.SessionStatus;
import com.xpec.c4.service.module.h5gateway.H5GatewayModule;
import com.xpec.c4.service.module.h5gateway.H5SessionStatus;
import com.xpec.c4.service.server.CloseType;
import com.xpec.c4.service.server.ServerOperations;
import com.xpec.c4.service.testcase.MessageQueue;

/**
 * 消息管理
 * 
 * @author Bany
 * 
 * @since 2008-8-30 下午05:44:18
 * 
 */
public class MessageAction 
{
	private static final Log log = LogFactory.getLog(MessageAction.class);
	
	/**
	 * @param messageType
	 * @param message
	 * 消息發送log
	 */
	private static void printSendLog(int messageType, Object message, GUID sessionId) {
		if (!log.isInfoEnabled()) {
			return;
		}

		String sessionStr = sessionId != null ? "s:" + sessionId.toString() : "s:null";
		String guidStr = "g:null";
		if (sessionId != null) {
			GUID playerId = PlayerCommonInfoManager.getInstance().getPlayerGuidBySession(sessionId);
			if (playerId != null) {
				guidStr = "g:" + playerId.toString();
			}
		}

		if(log.isDebugEnabled()){
			
			if(message instanceof BaseMessage){
				//只有在debug開啟時，才印出繼承BaseMessage的詳細內容
				BaseMessage LogTemp = (BaseMessage) message;
				log.debug("send message: " + ConstantCode.getName(MessageTypes.class, messageType) + " "
							+ messageType + LogTemp.toString() + " to " + guidStr + " " + sessionStr);	
			}
			else{
				log.debug("send message: " + ConstantCode.getName(MessageTypes.class, messageType) + " "
						+ messageType + " to " + guidStr + " " + sessionStr);
			}
			
		}
		else{
			
			log.info("send message: " + ConstantCode.getName(MessageTypes.class, messageType) + " "
					+ messageType + " to " + guidStr + " " + sessionStr);
			
		}
	}
	
	/**
	 * 顯示消息內帶的錯誤碼, 如果錯誤碼是 {@link ErrorCode#SUCCESS} 就不會顯示
	 * 
	 * @param messageType
	 *            {@link MessageTypes}
	 * @param message
	 *            實做 {@link IResultMessageBase} 的消息物件實體
	 */
	private static void printFailResult(int messageType, Object message) {
		if (!log.isErrorEnabled()) {
			return;
		}

		if (message instanceof IResultMessageBase) {
			IResultMessageBase resultMsg = (IResultMessageBase) message;
			if (resultMsg.getErrorCode() != ErrorCode.SUCCESS) {
				if (log.isErrorEnabled()) {
					String messageTypeName = ConstantCode.getNameWithCode(MessageTypes.class, messageType);
					String errorCodeName = ConstantCode.getNameWithCode(ErrorCode.class, resultMsg.getErrorCode());

					log.error("return " + messageTypeName + " error code: " + errorCodeName);
				}
			}
		}
	}

	/**
	 * 廣播消息給 client<br>
	 * 不包含機器人
	 * 
	 * @param messageType
	 * @param content
	 * @param broadCastTarget
	 *            廣播目標類型 {@link BroadcastTargetTypes}, 不指定 (填null) 預設會使用
	 *            {@link BroadcastTargetTypes#BROADCAST_TO_LOBBY}
	 */
	public static void broadCastMessage(int messageType,Object content,Integer broadCastTarget) 
	{		
		byte[] contentBytes = CodecUtility.contentEncode(content, messageType);

		GroupMessage groupMsg = new GroupMessage();
		groupMsg.setDestModuleID(ModuleName.GATEWAY);
		groupMsg.setMessageType(messageType);
		groupMsg.setMessageContentBytes(contentBytes);
		groupMsg.setToClient(true);
		groupMsg.setBroadCast(true);
		if(broadCastTarget != null){
			groupMsg.setBrocastTarget(broadCastTarget);
		}
		sendGroupMessage(groupMsg);

		groupMsg.setDestModuleID(ModuleName.H5GATEWAY);
		sendGroupMessage(groupMsg);
	}
	
	/**
	 * 廣播消息給 client<br>
	 * 包含機器人
	 * 
	 * @param messageType
	 * @param content
	 * @param broadCastTarget
	 *            廣播目標類型 {@link BroadcastTargetTypes}, 不指定 (填null) 預設會使用
	 *            {@link BroadcastTargetTypes#BROADCAST_TO_LOBBY}
	 */
	public static void broadCastMessageIncludeRobot(int messageType,Object content,Integer broadCastTarget) 
	{		
		byte[] contentBytes = CodecUtility.contentEncode(content, messageType);

		GroupMessage groupMsg = new GroupMessage();
		groupMsg.setDestModuleID(ModuleName.GATEWAY);
		groupMsg.setMessageType(messageType);
		groupMsg.setMessageContentBytes(contentBytes);
		groupMsg.setToClient(true);
		groupMsg.setBroadCast(true);
		groupMsg.setBroadcastToRobot(true);
		if(broadCastTarget != null){
			groupMsg.setBrocastTarget(broadCastTarget);
		}
		sendGroupMessage(groupMsg);

		groupMsg.setDestModuleID(ModuleName.H5GATEWAY);
		sendGroupMessage(groupMsg);
	}

	/**
	 * 廣播消息給在特定module內的 client<br>
	 * 不包含機器人
	 *
	 * @param messageType
	 * @param content
	 * @param moduleName (將會忽略大小寫的區別)
	 */
	public static void broadCastMessageToClientInCertainModule(int messageType, Object content, String moduleName) 
	{
		if(moduleName == null)
			return;
		
		byte[] contentBytes = CodecUtility.contentEncode(content, messageType);

		GroupMessage groupMsg = new GroupMessage();
		groupMsg.setDestModuleID(ModuleName.GATEWAY);
		groupMsg.setMessageType(messageType);
		groupMsg.setMessageContentBytes(contentBytes);
		groupMsg.setToClient(true);
		groupMsg.setBroadCast(true);
		//只對特定的module傳送
		groupMsg.setBrocastTarget(BroadcastTargetTypes.BROADCAST_TO_CERTAIN_MODULE);
		groupMsg.setBroadCastTargetModuleName(moduleName);
		
		sendGroupMessage(groupMsg);

		groupMsg.setDestModuleID(ModuleName.H5GATEWAY);
		sendGroupMessage(groupMsg);
	}
	
	/**
	 * 廣播消息給在特定module內的 client<br>
	 * 包含機器人
	 *
	 * @param messageType
	 * @param content
	 * @param moduleName (將會忽略大小寫的區別)
	 */
	public static void broadCastMessageToClientInCertainModuleIncludeRobot(int messageType, Object content, String moduleName) 
	{
		if(moduleName == null)
			return;
		
		byte[] contentBytes = CodecUtility.contentEncode(content, messageType);

		GroupMessage groupMsg = new GroupMessage();
		groupMsg.setDestModuleID(ModuleName.GATEWAY);
		groupMsg.setMessageType(messageType);
		groupMsg.setMessageContentBytes(contentBytes);
		groupMsg.setToClient(true);
		groupMsg.setBroadCast(true);
		//只對特定的module傳送
		groupMsg.setBrocastTarget(BroadcastTargetTypes.BROADCAST_TO_CERTAIN_MODULE);
		groupMsg.setBroadCastTargetModuleName(moduleName);
		groupMsg.setBroadcastToRobot(true);
		
		sendGroupMessage(groupMsg);

		groupMsg.setDestModuleID(ModuleName.H5GATEWAY);
		sendGroupMessage(groupMsg);
	}
	
	/**
	 * 发送消息
	 * 
	 * @param sessionIDs
	 *            要发送的sessionID列表
	 * @param messageType
	 *            消息类型
	 * @param message
	 *            消息实体
	 */
	public static void sendMessage(Collection<GUID> sessionIDs,
			int messageType, Object message) 
	{
		if (sessionIDs == null || sessionIDs.size() == 0) 
			return;

		HashMap<String, ArrayList<GUID>> gatewayMsgs = new HashMap<String, ArrayList<GUID>>();
		byte[] contentBytes = CodecUtility.contentEncode(message,messageType);
		
		if( sessionIDs.size() > 0 )
		{
			for (GUID sessionID : sessionIDs) 
			{
				if (sessionID == null) 
					continue;

				// session 是否為 h5 gateway
				String gatewayId = PlayerCommonInfoManager.getInstance().getGatewayBySessionId(sessionID);
				boolean isUseH5Gateway = ServerProperties.isTargetModule(gatewayId, ModuleName.H5GATEWAY);
				
				// 通过SESSION发送消息
				XSENetSession session = ServiceInfo.getSession(sessionID);
	
				// 如果有，就本地发送
				if (session != null) 
				{
					sendMessageByte(session, messageType, contentBytes);
				}
				else if (isUseH5Gateway) {
					H5XSENetSession h5Session = H5ServiceInfo.getSession(sessionID);
					sendMessageByte(h5Session, messageType, contentBytes);
				}
				// 给网关发送
				else 
				{
					String moduleName = PlayerCommonInfoManager.getInstance().getGatewayBySessionId(sessionID);
					if (moduleName != null) {
	
						ArrayList<GUID> ids = gatewayMsgs.get(moduleName);
						if (ids == null) 
						{
							ids = new ArrayList<GUID>();
							gatewayMsgs.put(moduleName, ids);
						}
						ids.add(sessionID);
					}
				}
			}
		}

		Set<Entry<String, ArrayList<GUID>>> entrySet = gatewayMsgs.entrySet();
		if( entrySet.size() > 0 )
		{
			for (Entry<String, ArrayList<GUID>> entry : entrySet) 
			{
				GroupMessage groupMsg = new GroupMessage();
				groupMsg.setDestModuleID(entry.getKey());
				groupMsg.setSessionIDs(entry.getValue());
				groupMsg.setMessageType(messageType);
				groupMsg.setMessageContentBytes(contentBytes);
				groupMsg.setToClient(true);
				//groupMsg.setBroadCast(true);
				sendGroupMessage(groupMsg);
			}
		}
	}
	
	/**
	 * @param ChannelID 	要發送的頻道ID(對頻道內的所有玩家)
	 * @param messageType	消息类型
	 * @param message		消息內容
	 * 
	 * 給頻道聊天的訊息用
	 */
	public static void sendMessage(int ChannelID,
			int messageType, Object message) 
	{		
		//debugLog
		printSendLog(messageType, message, null);
				
		byte[] contentBytes = CodecUtility.contentEncode(message, messageType);
		
		GroupMessage groupMsg = new GroupMessage();
		groupMsg.setDestModuleID(ModuleName.GATEWAY);
		groupMsg.setMessageType(messageType);
		groupMsg.setMessageContentBytes(contentBytes);
		groupMsg.setToClient(true);
		groupMsg.setChannelID(ChannelID);
		groupMsg.setBroadCast(true);
		groupMsg.setBrocastTarget(BroadcastTargetTypes.BROADCAST_TO_ALL);
		sendGroupMessage(groupMsg);

		groupMsg.setDestModuleID(ModuleName.H5GATEWAY);
		sendGroupMessage(groupMsg);
	}
	
	/**
	 * @param ChannelID 	要發送的頻道ID(對頻道內的所有玩家)
	 * @param NotSessionID	不傳送的sessionID
	 * @param messageType	消息类型
	 * @param message		消息內容
	 * 
	 * 給頻道聊天的訊息用
	 */
	public static void sendMessage(int ChannelID, GUID NotSessionID,
			int messageType, Object message) 
	{	
		//debugLog
		printSendLog(messageType, message, null);
				
		byte[] contentBytes = CodecUtility.contentEncode(message, messageType);
		
		GroupMessage groupMsg = new GroupMessage();
		groupMsg.setDestModuleID(ModuleName.GATEWAY);
		groupMsg.setMessageType(messageType);
		groupMsg.setMessageContentBytes(contentBytes);
		groupMsg.setToClient(true);
		groupMsg.setChannelID(ChannelID);
		groupMsg.setNotSessionID(NotSessionID);
		groupMsg.setBroadCast(true);
		groupMsg.setBrocastTarget(BroadcastTargetTypes.BROADCAST_TO_ALL);
		sendGroupMessage(groupMsg);

		groupMsg.setDestModuleID(ModuleName.H5GATEWAY);
		sendGroupMessage(groupMsg);
		
	}

	/**
	 * 发送消息
	 * 
	 * @param sessionID
	 *            要发送的sessionID
	 * @param messageType
	 *            消息类型
	 * @param message
	 *            消息实体
	 */
	public static void sendMessage(GUID sessionID, int messageType, Object message) 
	{
		//debugLog
		printSendLog(messageType, message, sessionID);
		printFailResult(messageType, message);
		
		//
		byte[] contentBytes = CodecUtility.contentEncode(message,messageType);
		
		NetMessage msg = new NetMessage(messageType, contentBytes, sessionID);

		// 通过SESSION发送消息
		sendMessage(msg);
	}

	/**
	 * 发送消息
	 * 
	 * @param message
	 */
	public static void sendMessage(NetMessage message) 
	{
		if (message == null) 
		{
			// log.error("message is null!");
			return;
		}
		
		//debugLog
		//有些消息會print兩次
		//printSendLog(message.getMessageType(), message);

		if (message.getSessionID() == null
				|| message.getSessionID().equals(new GUID()) == true) {
			return;
		}
		
		if( message.getMessageContentBytes() == null )
		{
			message.checkCreateMessageContentBytes();
		}
		
		int serviceInfoType = 0;
		
		String moduleId = PlayerCommonInfoManager.getInstance().getGatewayBySessionId(message.getSessionID());
		
		if (ServerProperties.isTargetModule(moduleId, ModuleName.H5GATEWAY)) {
			serviceInfoType = H5ServiceInfo.getModel();
		} else {
			serviceInfoType = ServiceInfo.getModel();
		}
		
		//
		switch (serviceInfoType) 
		{
		// 运行模式，发送消息
		case ServerModel.RUN:
			if (moduleId == null) {
//				return;
			}

			XSENetSession session = ServiceInfo.getSession(message.getSessionID());
			if (session != null) 
			{
				try 
				{
					executeClientMessage(message, session);
				} 
				catch (Exception e) 
				{
					if (log.isErrorEnabled()) {
						log.error("message type:" + message.getMessageType(), e);						
					}
				}

			} 
			else if (ModuleServer.getInstance().getBasicServiceModule(moduleId) instanceof H5GatewayModule) {
				H5XSENetSession h5Session = H5ServiceInfo.getSession(message.getSessionID());
				if (h5Session != null) 
				{
					try 
					{
						executeClientMessage(message, h5Session);
					} 
					catch (Exception e) 
					{
						if (log.isErrorEnabled()) {
							log.error("message type:" + message.getMessageType(), e);						
						}
					}
	
				} 
			}
			else 
			{
				GUID sessionID = message.getSessionID();
				
				//取得session所在gateway 並設定
				message.setModuleID(moduleId);
				
				if (moduleId != null) {
					// 通过模块ID判定，模块是否在本SERVER，如果不在则转发给相应组员
					if (!ModuleServer.getInstance().getModuleNames().contains(moduleId)) 
					{
						GroupMessage gm = new GroupMessage(message);
						sendGroupMessage(gm);
					}
					else // 如果是本SERVER，则调用MessegAction.sendClientMessage进行发送
					{
						sendClientMessage(message);
					}
				}else{
					if (log.isErrorEnabled()) {
						log.error("PlayerCommonInfoManager.getGatewayBySessionId(" + sessionID + ") fail.");
					}
				}
			}

			break;

		// 测试用例模式，把消息压到容器内
		case ServerModel.TESTCASE:
			MessageQueue.add(message);
			break;
		}

	}

	/**
	 * 发送消息
	 * 
	 * @param message
	 */
	public static void sendMessage(GroupMessage message) 
	{
		sendGroupMessage(message);
	}
	
	/**
	 * 發送消息並等待回應(Blocking)
	 *   會進入同步執行緒區間，讓另外的執行緒先處理下一個訊息
	 *   因此返回時，需注意原執行緒參照的資料可能已被其他執行緒更新的問題
	 */
	public static Object sendMessageWaitResponse(GroupMessage message) {
		return sendMessageWaitResponse(message, null);
	}
	
	/**
	 * 發送消息並等待回應(Blocking)
	 *   會進入同步執行緒區間，讓另外的執行緒先處理下一個訊息
	 *   因此返回時，需注意原執行緒參照的資料可能已被其他執行緒更新的問題
	 */
	public static Object sendMessageWaitResponse(GroupMessage message,
			Long timeout) {
		BasicServiceModule curModule = Contexts.getModule();
		if(curModule != null) {
			Contexts.getModule().EnterSyncCallSection();
		}
		Object returnObj = ServerGroup.getTransportManager()
				.sendMessageWaitResponse(message, timeout);
		if(curModule != null) {
			Contexts.getModule().ExitSyncCallSection();
		}
		return returnObj;
	}

	/**
	 * 直接发送消息给Client
	 * 
	 * @param message
	 * 
	 * @author lipeilin
	 * @2011-3-14 上午11:49:39
	 */
	private static void sendClientMessage(NetMessage message) 
	{
		if (message == null) {
			return;
		}
		
		if (!(ModuleServer.getInstance().getBasicServiceModule(message.getModuleID()) instanceof H5GatewayModule)) {
			XSENetSession session = ServiceInfo.getSession(message.getSessionID());
			if (session == null){
				// 這個發生的原因可能在底層已經斷線但是遊戲邏輯還沒斷。
				// 所以還會有繼續對該玩家送消息的可能。
				if(log.isErrorEnabled()){
					log.error("session is null:" + message.getMessageType());
				}
				return;
			}
			try 
			{
				executeClientMessage(message, session);
			} 
			catch (Exception e) 
			{
				if (log.isErrorEnabled()) {
					log.error("message type:" + message.getMessageType()
							/* + "\tmessage object:"+ message.getMessageContentBytes() */,
							e);
				}
			}
		} else {
			H5XSENetSession h5Session = H5ServiceInfo.getSession(message.getSessionID());
			if (h5Session == null){
				// 這個發生的原因可能在底層已經斷線但是遊戲邏輯還沒斷。
				// 所以還會有繼續對該玩家送消息的可能。
				if(log.isErrorEnabled()){
					log.error("session is null:" + message.getMessageType());
				}
				return;
			}
			try 
			{
				executeClientMessage(message, h5Session);
			} 
			catch (Exception e) 
			{
				if (log.isErrorEnabled()) {
					log.error("message type:" + message.getMessageType()
							/* + "\tmessage object:"+ message.getMessageContentBytes() */,
							e);
				}
			}
		}
	}

	/**
	 * 执行发送CLIENT消息的事件
	 * 
	 * @param message
	 * @param session
	 *
	 * @2011-12-3 下午03:36:27
	 */
	private static void executeClientMessage(NetMessage message, XSENetSession session) 
	{
		new MessageSentTask(session, message, message.getPriority()).run();
	}
	private static void executeClientMessage(NetMessage message, H5XSENetSession h5Session) 
	{
		new MessageSentTask(h5Session, message, message.getPriority()).h5Run();
	}
	
	/**
	 * 执行发送GROUP消息的事件
	 * 
	 * @param message
	 * @param session
	 *
	 * @2011-12-3 下午03:36:27
	 */
	private static void executeGroupMessage(GroupMessage message) 
	{
		new GroupMessageSentTask(message).run();
	}

	/**
	 * 
	 * @param sessionID
	 * @param messageType
	 * @param contentBytes
	 */
	@SuppressWarnings("unused")
	private static void sendClientMessage(GUID sessionID, int messageType,
			byte[] contentBytes) 
	{
		if (sessionID == null) 
		{
			return;
		}

		if (!(ModuleServer.getInstance().getBasicServiceModule(PlayerCommonInfoManager.getInstance().getGatewayBySessionId(sessionID)) instanceof H5GatewayModule)) {
			XSENetSession session = ServiceInfo.getSession(sessionID);
	
			if (contentBytes == null || session == null) {
				if (log.isDebugEnabled()) {
					log.debug("message or session is null:" + messageType);
				}
				return;
			}
	
			//
			try {
				NetMessage msg = new NetMessage(messageType, contentBytes,
						sessionID);
				executeClientMessage(msg, session);
			} catch (Exception e) {
				if (log.isErrorEnabled()) {
					log.error("message type:" + messageType /*
															 * + "\tmessage object:"
															 * + contentBytes
															 */, e);
				}
			}
				
		} else {
			H5XSENetSession h5Session = H5ServiceInfo.getSession(sessionID);
	
			if (contentBytes == null || h5Session == null) {
				if (log.isDebugEnabled()) {
					log.debug("message or session is null:" + messageType);
				}
				return;
			}
	
			//
			try {
				NetMessage msg = new NetMessage(messageType, contentBytes,
						sessionID);
				executeClientMessage(msg, h5Session);
			} catch (Exception e) {
				if (log.isErrorEnabled()) {
					log.error("message type:" + messageType /*
															 * + "\tmessage object:"
															 * + contentBytes
															 */, e);
				}
			}
			
		}
	}

	/**
	 * 直接消息给组成员
	 * 
	 * @param message
	 * 
	 * @author lipeilin
	 * @2011-3-14 上午11:49:39
	 */
	public static void sendGroupMessage(GroupMessage message) throws GroupTransportException 
	{
		String moduleName = message.getDestModuleID();
		if (GameUtility.isHaveString(moduleName)) {
			if (ServerProperties.isTargetModule(moduleName, ModuleName.GATEWAY) && ServerProperties.isHaveNormalGateway()) {
				executeGroupMessage(message);
				return;
			}
			if (ServerProperties.isTargetModule(moduleName, ModuleName.H5GATEWAY) && ServerProperties.isHaveH5Gateway()) {
				executeGroupMessage(message);
				return;
			}
			executeGroupMessage(message);
		} else {
			executeGroupMessage(message);
		}
	}

	/**
	 * 指发送消息
	 * 
	 * @param messages
	 */
	public static void sendMessage(Collection<NetMessage> messages) 
	{
		for (NetMessage netMessage : messages) 
		{
			sendMessage(netMessage);
		}
	}
	
	/**
	 * 直接消息给组成员，等待返回
	 * 
	 * @param message
	 * 
	 * @author lipeilin
	 * @2011-3-14 上午11:49:39
	 */
	@SuppressWarnings("unchecked")
	protected static List<Object> sendGroupMessageWaitAllResponse(GroupMessage message) 
	{
		Object resonse = ServerGroup.getTransportManager().sendMessage(
							message,
							RequestOptions.SYNC() );
		List<Object> responseList;
		if (resonse instanceof List)
		{
			responseList = (List<Object>) resonse;
		} 
		else 
		{
			responseList = new ArrayList<Object>();
			responseList.add(resonse);
		}
		return responseList;
	}

	/**
	 * 发送消息。内容为已经编码的字节数组
	 * 
	 * @param session
	 *            发送的session
	 * @param messageType
	 *            消息类型
	 * @param message
	 *            消息内容实体编码后的内容
	 */
	public static void sendMessageByte(XSENetSession session, int messageType, byte[] messageBytes) 
	{
		if (session == null)  return;

		// 构造消息
		NetMessage netMessage = new NetMessage(messageType, messageBytes, session.getID());
	
		executeClientMessage(netMessage, session);
	}
	public static void sendMessageByte(H5XSENetSession h5Session, int messageType, byte[] messageBytes) 
	{
		if (h5Session == null)  return;

		// 构造消息
		NetMessage netMessage = new NetMessage(messageType, messageBytes, h5Session.getID());
	
		executeClientMessage(netMessage, h5Session);
	}
	
	/**
	 * 直接发送消息给Client。内容为已经编码的字节数组
	 * 
	 * @param message
	 * 
	 * @author lipeilin
	 * @2011-3-14 上午11:49:39
	 */
	public static void sendClientMessageBytes(Collection<GUID> sessionIDs, int messageType, byte[] message) 
	{
		if (sessionIDs == null || sessionIDs.size() == 0) 
		{
			return;
		}

		for (GUID sessionID : sessionIDs) 
		{
			if (ServerProperties.isTargetModule(PlayerCommonInfoManager.getInstance().getGatewayBySessionId(sessionID), ModuleName.H5GATEWAY)) {
				H5XSENetSession session = H5ServiceInfo.getSession(sessionID);
				if (session != null) 
				{
					sendMessageByte(session, messageType, message);
				}
			} else {
				XSENetSession session = ServiceInfo.getSession(sessionID);
				if (session != null) 
				{
					sendMessageByte(session, messageType, message);
				}
			}
		}
	}
	
	/**
	 * 直接发送消息给Client。内容为已经编码的字节数组
	 * 
	 * @param message
	 * 
	 * @author lipeilin
	 * @2011-3-14 上午11:49:39
	 */
	public static void sendClientMessageBytes(Collection<GUID> sessionIDs, GUID notSessionID, int messageType, byte[] message) 
	{
		if (sessionIDs == null || sessionIDs.size() == 0) 
		{
			return;
		}

		for (GUID sessionID : sessionIDs) 
		{
			if(sessionID.equals(notSessionID)){
				//過濾不傳的人
				continue;
			}
			if (ServerProperties.isTargetModule(PlayerCommonInfoManager.getInstance().getGatewayBySessionId(sessionID), ModuleName.H5GATEWAY)) {
				H5XSENetSession session = H5ServiceInfo.getSession(sessionID);
				if (session != null) 
				{
					sendMessageByte(session, messageType, message);
				}
			} else {
				XSENetSession session = ServiceInfo.getSession(sessionID);
				if (session != null) 
				{
					sendMessageByte(session, messageType, message);
				}
			}
		}
	}
	
	/**
	 * 掃一遍所有的連線，發送廣播消息給 client
	 * @param msg
	 * @param messageType
	 * @param message
	 */
	public static void boardCastMessage(GroupMessage msg, int messageType, byte[] message)
	{
		// 想辦法把gateway抓出來 XD
		String moduleId = msg.getDestModuleID();
		if (moduleId != null) {
			BasicServiceModule module = ModuleServer.getInstance()
					.getBasicServiceModule(moduleId);
			if (module != null && module instanceof GatewayModule) {
				broadcastGatewayMessage(module, msg, messageType, message);
			} else if (module != null && module instanceof H5GatewayModule) {
				if (MessageTypes.messageMap.get(messageType).isH5Enable()) {
					broadcastH5GatewayMessage(module, msg, messageType, message);
				}
			}
		}
	}
	
	public static void broadcastGatewayMessage(BasicServiceModule module, GroupMessage msg, int messageType, byte[] message) {
		GatewayModule gateway = (GatewayModule) module;

		// Collection<XSENetSession> allSessions = gateway.getAllSession();
		Collection<SessionStatus> allSessions2 = gateway.getAllSession2();
				
		if (allSessions2 != null) {
			int count = 0;
				// 掃一遍所有的連線
				for (Entry<GUID, SessionStatus> entry : gateway.getSessions2().entrySet()) {
					SessionStatus it = entry.getValue();
					if (it == null) {
						if (log.isErrorEnabled()) {
							log.error("boardCastMessage it = null");
						}
					}
							
					try {
						if (it != null && it.canSend && it.session != null) {
							boolean isRobotSession = false;
							if (it.session.getParameters().containsKey(ServerOperations.IS_ROBOT)) {
								isRobotSession = (boolean) it.session.getParameters().get(ServerOperations.IS_ROBOT);
							}

							// 是機器人連線 && 沒有要廣播給機器人
							if (isRobotSession && !msg.isBroadcastToRobot()) {
								continue;
							}

							if(msg.getChannelID() > 0){
								// no chat module
							}else{
								if(msg.getBrocastTarget() == BroadcastTargetTypes.BROADCAST_TO_ALL){
									sendMessageByte(it.session, messageType, message);
								}else{
									String zoneId = PlayerCommonInfoManager.getInstance().getZoneIdBySession(it.session.getID());
										
									//預設只傳在大廳的
									if (GameUtility.isHaveString(zoneId)) {
										switch (msg.getBrocastTarget()) {
											//GroupMessage初始值就是LOBBY
										case BroadcastTargetTypes.BROADCAST_TO_LOBBY:{
											if (ServerProperties.isTargetModule(zoneId, ModuleName.GAMELOBBY)) {
												sendMessageByte(it.session, messageType, message);
											}
											break;
										}
										case BroadcastTargetTypes.BROADCAST_TO_CERTAIN_MODULE:{
											//取得目標module名稱
											String targetModuleName = msg.getBroadCastTargetModuleName();
											if(GameUtility.isHaveString(targetModuleName)){
												if(ServerProperties.isTargetModule(zoneId, targetModuleName)){
													sendMessageByte(it.session, messageType, message);
												}
											}
											break;
										}
										default:
											break;
										}
									}
								}
							}	
						}
					} catch (Exception e) {
						if (log.isErrorEnabled()) {
							log.error("boardCastMessage fail, ChannelId=" + msg.getChannelID(), e);
						}
					}

					count++;

					if (count % 100 == 0) {
						try {
							Thread.sleep(100);
						} catch (InterruptedException e) {
							if (log.isErrorEnabled()) {
								log.error("boardCastMessage fail.",e);
							}
						}
					}
				}

			}
		
	}
	
	public static void broadcastH5GatewayMessage(BasicServiceModule module, GroupMessage msg, int messageType, byte[] message) {
		H5GatewayModule h5gateway = (H5GatewayModule) module;

		// Collection<XSENetSession> allSessions = gateway.getAllSession();
		Collection<H5SessionStatus> allSessions2 = h5gateway.getAllSession2();
				
		if (allSessions2 != null) {
			int count = 0;
				// 掃一遍所有的連線
				for (Entry<GUID, H5SessionStatus> entry : h5gateway.getSessions2().entrySet()) {
					H5SessionStatus it = entry.getValue();
					if (it == null) {
						if (log.isErrorEnabled()) {
							log.error("boardCastMessage it = null");
						}
					}
							
					try {
						if (it != null && it.canSend && it.session != null) {
							boolean isRobotSession = false;
							if (it.session.getParameters().containsKey(H5ServerOperations.IS_ROBOT)) {
								isRobotSession = (boolean) it.session.getParameters().get(H5ServerOperations.IS_ROBOT);
							}

							// 是機器人連線 && 沒有要廣播給機器人
							if (isRobotSession && !msg.isBroadcastToRobot()) {
								continue;
							}

							if(msg.getChannelID() > 0){
								// no chat module
							}else{
								if(msg.getBrocastTarget() == BroadcastTargetTypes.BROADCAST_TO_ALL){
									sendMessageByte(it.session, messageType, message);
								}else{
									String zoneId = PlayerCommonInfoManager.getInstance().getZoneIdBySession(it.session.getID());
										
									//預設只傳在大廳的
									if (GameUtility.isHaveString(zoneId)) {
										switch (msg.getBrocastTarget()) {
											//GroupMessage初始值就是LOBBY
										case BroadcastTargetTypes.BROADCAST_TO_LOBBY:{
											if (ServerProperties.isTargetModule(zoneId, ModuleName.GAMELOBBY)) {
												sendMessageByte(it.session, messageType, message);
											}
											break;
										}
										case BroadcastTargetTypes.BROADCAST_TO_CERTAIN_MODULE:{
											//取得目標module名稱
											String targetModuleName = msg.getBroadCastTargetModuleName();
											if(GameUtility.isHaveString(targetModuleName)){
												if(ServerProperties.isTargetModule(zoneId, targetModuleName)){
													sendMessageByte(it.session, messageType, message);
												}
											}
											break;
										}
										default:
											break;
										}
									}
								}
							}	
						}
					} catch (Exception e) {
						if (log.isErrorEnabled()) {
							log.error("boardCastMessage fail, ChannelId=" + msg.getChannelID(), e);
						}
					}

					count++;

					if (count % 100 == 0) {
						try {
							Thread.sleep(100);
						} catch (InterruptedException e) {
							if (log.isErrorEnabled()) {
								log.error("boardCastMessage fail.",e);
							}
						}
					}
				}

			}
			
	}

	/**
	 * 模擬 client 送出消息觸發消息處理事件，開發初期還沒跟 client 對接之前的功能可以用這個測試。
	 * 
	 * @param player
	 *            送消息的玩家資料
	 * @param moduleId
	 *            接收消息的 module id
	 * @param messageType
	 *            消息編號
	 * @param messageContent
	 *            消息內容 class
	 */
	public static void sendFakeClientMessage(Player player, String moduleId, int messageType, Object messageContent) {
		// 取得指定的 module
		GroupMessage msg = new GroupMessage();
		msg.setDestModuleID(moduleId);
		msg.setMessageType(messageType);
		msg.setMessageContent(messageContent);
		msg.setSessionID(player.getSessionID());
		msg.setSrcService(player.getZoneId());
		MessageAction.sendGroupMessage(msg);
		
//		ModuleServer moduleServer = ModuleServer.getInstance();
//		BasicServiceModule basicModule = moduleServer.getBasicServiceModule(moduleId);

//		// 填入消息參數
//		int syncId = 0;
//		byte[] messageContentBytes = CodecUtility.encodeByte(messageContent, messageType);
//		GUID srcSessionId = player.getSessionID();
//		String srcService = player.getZoneId();
//		Address srcAddress = null;
//		int options = 0;
//
//		// 加入 module 的消息處理佇列中
//		basicModule.addMessage(messageType, syncId, messageContentBytes, srcSessionId, srcService, srcAddress,
//				options);
	}

	/**
	 * 發強制公告給client 
	 */
	public static void sendForciblyBulletinToClient(ForciblyBulletinInfo info){
		if(info == null){
			return;
		}
		broadCastMessage(MessageTypes.S_ZONE_FORCIBLY_BULLETIN, info , BroadcastTargetTypes.BROADCAST_TO_ALL);
	}
	/**
	 * 快速傳送帶有錯誤碼的結果消息給 client
	 * 
	 * @param sessionId
	 *            client session id
	 * @param messageType
	 *            消息編號 {@link MessageTypes}
	 * @param c
	 *            消息內夾帶的資料 class, class 必須實做 {@link IResultMessageBase}
	 * @param errorCode
	 *            要回傳的錯誤碼
	 */
	public static void sendFailResultClientMessage(GUID sessionId, int messageType,
			Class<? extends IResultMessageBase> c, int errorCode) {
		if (c == null) {
			return;
		}

		Constructor<?> ctor;
		IResultMessageBase object = null;
		try {
			ctor = c.getConstructor();
			object = (IResultMessageBase) ctor.newInstance();
		} catch (Exception e) {
			if (log.isErrorEnabled()) {
				log.error("sendFailResultClientMessage() fail.", e);
				return;
			}
		}
		object.setErrorCode(errorCode);

		sendMessage(sessionId, messageType, object);
	}
}

/**
 * 
 * @author id 9527
 *
 */
class MessageSentTask
{
	// private static final Log log = LogFactory.getLog(MessageSentTask.class);

	private XSENetSession session;

	private H5XSENetSession h5Session;

	private NetMessage gameMessage;

	private int priority;

	public MessageSentTask(XSENetSession session, NetMessage gameMessage, int priority) 
	{
		this.session = session;
		this.gameMessage = gameMessage;
		this.priority = priority;
	}

	public MessageSentTask(H5XSENetSession h5Session, NetMessage gameMessage, int priority) 
	{
		this.h5Session = h5Session;
		this.gameMessage = gameMessage;
		this.priority = priority;
	}

	public void run() 
	{
		session.send(gameMessage, priority);
	}

	public void h5Run() 
	{
		h5Session.send(gameMessage, priority);
	}
}

/**
 * 
 * @author id 9527
 *
 */
class GroupMessageSentTask
{
	private static final Log log = LogFactory.getLog(GroupMessageSentTask.class);

	private GroupMessage message;

	public GroupMessageSentTask(GroupMessage gameMessage) 
	{
		this.message = gameMessage;
	}

	public void run() 
	{
		try 
		{
			ServerGroup.getTransportManager().sendMessage(message);
		} 
		catch (GroupTransportException e) 
		{
			if (log.isErrorEnabled()) {
				log.error("send message error :" + message, e);
			}
		}
		catch (Exception e) 
		{
			if (log.isErrorEnabled()) {
				log.error("send message error:" + message, e);
			}
			
			GUID sessionID = message.getSessionID();
			if (sessionID != null) 
			{
				if (log.isErrorEnabled()) {
					log.error("kick session by send message error!");
				}
				if (ServerProperties.isTargetModule(message.getDestModuleID(), ModuleName.H5GATEWAY)) {
					H5ServerOperations.sessionClose(sessionID, CloseType.KICK_BY_ERROR, null);
				} else {
					ServerOperations.sessionClose(sessionID, CloseType.KICK_BY_ERROR, null);
				}
			}
		}
	}
}
